﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Runtime.CompilerServices;

using alib;
using alib.Collections;
using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// In accordance with DELPH-IN tradition, we distinguish "types" from "entries." (Copestake 2002 p.106)
	/// Entries include everything except Types: lexical entries, grammar rules, start symbols, and node labels.
	/// In common, both share this abstract base class, Instance, which captures the idea of something that can
	/// have constraints. Types participate in a DAG hierarchy whereas Entries instead refer to a single Type. Thus,
	/// while Types and Entries have an is-a (inheritance) relationship with Instance, every Entry additionally has
	/// a has-a (contains) relationship with exactly one Type.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class Instance : seq_typedict_named, IInstance,
		schema.ρρTdl,
		schema.ρρTdlType
	{
		protected Instance(sync_obj_ctor_arg _) : base(_) { }
		protected Instance(IRtParent parent, Grammar g, String name)
			: base(parent, default(IIdentity), g, name)
		{
		}

		public virtual void Initialize(ConsDefs cd)
		{
			this.fp = cd.identifier.FilePos;
			this.cb_tdl = cd.cch;

			if (cd.Count > 0)
				SetConstraints(cd);

			if ((this is Type) != (cd.tdl_type == TdlType.Type))
				throw new Exception();
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public int _id;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public abstract Type InstanceType { get; }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[Flags]
		public enum Flags
		{
			TopType /*					*/	= 0x00000001,
			/// <summary>set if this type is a system-generated greater lowest bound (glb) type</summary>
			GlbType /*					*/	= 0x01000000,

			/// <summary>type has at least one or more inherited or local appropriate feature(s), 
			/// regardless of whether there is a feature structure in its definition  </summary>
			HasAppropriateFeatures /*	*/	= 0x00000100,

			/// <summary>type definition has appropriate features and a (possibly vacuous) authored feature 
			/// structure is locally present </summary>
			HasLocalConstraints /*		*/	= 0x00000200,

			/// <summary>May introduce features via unification, even if the instance does not have any 
			/// appropriate features itself. Knowing the presence of this condition may help determine
			/// an order for expanding the types which reduces recursive expansion</summary>
			FeaturesBelow /*			*/	= 0x00000800,
		};

		public Flags m_flags;

		public bool IsTop { get { return (m_flags & Flags.TopType) != 0; } }
		public bool IsGlb { get { return (m_flags & Flags.GlbType) != 0; } }
		public bool HasAppropriateFeatures { get { return (m_flags & Flags.HasAppropriateFeatures) != 0; } }
		public bool HasLocalConstraints { get { return (m_flags & Flags.HasLocalConstraints) != 0; } }
		public bool HasFeaturesBelow { get { return (m_flags & Flags.FeaturesBelow) != 0; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// pointer to the source TDL file
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public FilePos fp;
		public FilePos FilePos { get { return fp; } }
		int cb_tdl;

		public byte[] TdlBytes
		{
			get
			{
				if (fp.file == null || fp.offs == -1)
					return alib.Collections.Collection<byte>.None;
				byte[] arr = new byte[cb_tdl];
				using (var sr = System.IO.File.OpenRead(fp.file))
				{
					sr.Seek(fp.offs, System.IO.SeekOrigin.Begin);
					sr.Read(arr, 0, cb_tdl);
				}
				return arr;
			}
		}
		public String TdlText { get { return System.Text.Encoding.UTF8.GetString(TdlBytes); } }

		public Tdl Tdl
		{
			get { return new Tdl(this, TdlText); }
			set { throw not.valid; }
		}

		public TdlType TdlType
		{
			get { return (TdlType)(_id >> 24); }
			set { throw not.valid; }
		}

		public int SubIndex { get { return _get_sub_index(_id); } }

		public static int _get_sub_index(int id) { return id & 0x00FFFFFF; }

		public static TdlType TdlTypeFromId(int id, out int sub_ix)
		{
			sub_ix = _get_sub_index(id);
			return (TdlType)(id >> 24);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public ConsDefs m_consdefs;

		public void SetConstraints(ConsDefs consdef)
		{
			Debug.Assert(consdef != null);

			if (this.m_consdefs == null)
				this.m_consdefs = consdef;
			else
				this.m_consdefs.AddRange(consdef);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerDisplay("{_definition!=null?_definition.ToString():\"(TFS definition not loaded yet)\",nq}")]
		protected Tfs _definition;

		[DebuggerDisplay("{_expanded is BusyTfs?\"(expanding...)\":_expanded==null?\"(TFS not expanded)\":_expanded.ToString(),nq}")]
		public Tfs _expanded;

		protected abstract Tfs EnsureExpanded();

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsExpanded { get { var _tmp = _expanded; return _tmp != null && !(_tmp is BusyTfs); } }

		/// <summary>
		/// To avoid double work, we (attempt to) atomically install an 'ExpandingTfs' placeholder asserting our claim.
		/// That object serves as the blocking event for anyone else who tries to duplicate the effort.
		/// </summary>
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Tfs Expanded
		{
			get
			{
				/// demand entries do things their own way (i.e. keep weak references, don't use '_expanded' field
				if (this is DemandExpandEntry)
					return ((DemandExpandEntry)this).EnsureExpanded();

				Tfs _tmp;
				if ((_tmp = _expanded) == null && (new BusyTfs(Grammar)).InterlockedTryClaim(ref _expanded, out _tmp))
				{
					_tmp = EnsureExpanded();
					_tmp.flags |= Tfs.Flags.Expanded;
					_tmp.InterlockedPublishTo(ref _expanded);
				}
				else if (_tmp is BusyTfs)
					_tmp = _tmp.InterlockedWaitPulse(ref _expanded);

				return _tmp;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]	// side effects
		public Tfs Definition
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
			get
			{
				var _tmp = _definition;
				return _tmp ?? _load_definition(_tmp);
			}
		}

		Tfs _load_definition(Tfs _tmp)
		{
			TfsBuilder.Tdl tfs_tdl = null;
			int ec;
			if (m_consdefs == null || 0 == (ec = (tfs_tdl = TfsBuilder.Tdl.Build(bcpo, ftm.r_none, m_consdefs, InstanceType)).EdgeCount))
			{
#if true || _ATFS_DIRECT
				Type t = this as Type;
				if (Name == "cons" || Name == "*cons*")
				{
					_tmp = new ArrayTfs(em, tfs_tdl, Tfs.Flags.RootCoverage);
				}
				else if (t != null)
				{
					_tmp = new EmptyFeatureStructureTfs(Grammar, t);
					Debug.Assert(!this.HasLocalConstraints);
				}
				else
				{
					_tmp = InstanceType.Definition;
					this.m_flags |= (InstanceType.m_flags & Flags.HasLocalConstraints);
				}
#else
				_tmp = this is Type ? new EmptyFeatureStructureTfs((Type)this) : InstanceType.Definition;
#endif
			}
			else
			{
				_tmp = new ArrayTfs(em, tfs_tdl, Tfs.Flags.RootCoverage);
				Debug.Assert(_tmp.EdgeCount >= tfs_tdl.EdgeCount);

				this.m_flags |= Flags.HasLocalConstraints;
			}

			if (_tmp == (Interlocked.CompareExchange(ref _definition, _tmp, null) ?? _tmp))
			{
				m_consdefs = null;
				if (tfs_tdl != null)
					discarding_tdl(tfs_tdl);
			}

			return _tmp;
		}

		protected virtual void discarding_tdl(TfsBuilder.Tdl tfs_tdl) { }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static class Compare
		{
			public static class Id
			{
				public static readonly IComparer<Instance> Forward = new _fwd();
				public static readonly IComparer<Instance> Reverse = new _rev();
				sealed class _fwd : IComparer<Instance> { public int Compare(Instance x, Instance y) { return x._id - y._id; } }
				sealed class _rev : IComparer<Instance> { public int Compare(Instance x, Instance y) { return y._id - x._id; } }
			};
		};

		public override String ToString()
		{
			return this.Name ?? "[" + GetType().Name + "]";
		}
	};
}
